﻿package mlc.haxe.input.parsing;
import mlc.haxe.Console;
using mlc.haxe.input.parsing.GetIndexOf;

/**
 * An input parser, that parses all the input that comes into the console.
 */
class InputParser {
	private var console:Console;
	private var escapeCharacters:Array<String>;
	public function new(console:Console):Void {
		this.console = console;
		escapeCharacters = new Array();
		escapeCharacters.push("\\");
	}
	private function convertArgumentsToStrings(value:Array<Argument>):Array<String> {
		var result:Array<String> = new Array();
		for (argument in value) {
			if (argument.isOneArgument) {
				result.push(argument.value);
			} else {
				var words:Array<String> = argument.value.split(" ");
				for (word in words) {
					result.push(word);
				}
			}
		}
		return result;
	}
	/**
	 * Checks if the input contains quotes.
	 */
	private function determineContainsQuotes(value:String):Bool {
		for (index in 0...value.length) {
			if (value.charAt(index) == "\"" && determineIsEscapeCharacter(value.charAt(index - 1)) == false) {
				return true;
			}
		}
		return false;
	}
	/**
	 * Checks if the input contains an even number of quotes.
	 */
	private inline function determineIsCorrectNumberOfQuotes(value:String):Bool {
		var numberOfQuotes:Int = 0;
		for (index in 0...value.length) {
			if (value.charAt(index) == "\"") {
				numberOfQuotes++;
			}
		}
		return numberOfQuotes & 1 == 0;
	}
	private function determineIsEscapeCharacter(value:String):Bool {
		for (character in escapeCharacters) {
			if (value == character) {
				return true;
			}
		}
		return false;
	}
	/**
	 * Parses some raw input into chopped pieces.
	 */
	public function parseInput(value:String):Array<String> {
		console.write("\"" + value + "\"", 0x00FF00, true);
		var words:Array<String> = value.split(" ");
		if (determineContainsQuotes(value)) {
			if (determineIsCorrectNumberOfQuotes(value) == false) {
				console.write("Incorrect number of quotes.", 0xFF0000, true);
			} else {
				words = convertArgumentsToStrings(splitOnQuotes(value));
			}
		}
		var result:Array<String> = new Array();
		for (word in words) {
			result.push(stripFromEscapeCharacters(word));
		}
		return result;
	}
	private inline function splitOnQuotes(value:String):Array<Argument> {
		var result:Array<Argument> = new Array();
		var index:Int = 0;
		while (value.length > 0) {
			var argument:Argument;
			if (value.charAt(0) == "\"") {
				argument = new Argument(value.substr(1, value.getIndexOf("\"", 1, escapeCharacters) - 1), true);
				value = value.substr(2 + argument.value.length);
			} else {
				if (determineContainsQuotes(value)) {
					argument = new Argument(value.substr(0, value.getIndexOf("\"", 0, escapeCharacters)), false);
					value = value.substr(argument.value.length);
				} else {
					argument = new Argument(value, false);
					value = "";
				}
			}
			argument.value = StringTools.trim(argument.value);
			result.push(argument);
		}
		return result;
	}
	private inline function stripFromEscapeCharacters(value:String):String {
		var result:String = "";
		for (index in 0...value.length) {
			if (determineIsEscapeCharacter(value.charAt(index)) == false || determineIsEscapeCharacter(value.charAt(index - 1))) {
				result += value.charAt(index);
			}
		}
		return result;
	}
}
class Argument {
	public var isOneArgument(default, null):Bool;
	public var value:String;
	public function new(value:String, isOneArgument:Bool):Void {
		this.value = value;
		this.isOneArgument = isOneArgument;
	}
}